/*
******************************************************************************************************* 
**  Copyright (C) 2019, 苏州检易生物科技有限公司 
**  All rights reserved. 
** 
**  FileName:       	log_console.c
**  Description:	
**  Author:        	 
**  Version				v0.1
**  Date:           	2019-05-17
**	Function List： 
**  History:         
*********************************************************************************************************
*/


/*
*********************************************************************************************************
*                                              	调试开关
*********************************************************************************************************
*/
//using namespace std;

/*
*********************************************************************************************************
*                                             INCLUDE FILES
*********************************************************************************************************
*/
#include "log_console.h"
#ifdef RTOS
#include <rtthread.h>
#include <rthw.h>
#endif
/*
*********************************************************************************************************
*                                              	宏定义
*********************************************************************************************************
*/



/*
*********************************************************************************************************
*                                              	结构体定义
*********************************************************************************************************
*/

 
/*
*********************************************************************************************************
*                                              	函数原型声明
*********************************************************************************************************
*/
static void log_console_tc_callback(void* arg,uint8_t * txbuff,uint16_t tx_len);

static void log_console_start_transfer(log_console_t*);
/*
*********************************************************************************************************
*                                              	全局变量定义
*********************************************************************************************************
*/
extern __IO uint64_t LocalTime;
/*
*********************************************************************************************************
*                                              	模块静态变量定义
*********************************************************************************************************
*/
static log_tx_callback console_tx_callback = NULL;
/* log 前缀 */
const  static  char *log_head_group[LEVEL_SIZE] = {"[ERROR]","[CRITICAL]","[TRACE]","[DEBUG]"};

/* log 前缀的长度 */
const  static   uint8_t log_head_len[LEVEL_SIZE] = {7,10,7,7};

char log_cache[LOG_TTY_BUF_SIZE] = {0x0};

log_console_t m_log_console;

__attribute__((section(".RAM_D2"))) static uint8_t log_console_mem[LOG_CONSOLE_TX_MEM_SIZE];
//__attribute__((section(".RAM_D2"))) static uint8_t log_console_rxbuff[LOG_CONSOLE_RX_MEM_SIZE];
/*
*********************************************************************************************************
*                                              	函数定义
*********************************************************************************************************
*/
void log_console_init(void)
{
	memset(log_console_mem,LOG_CONSOLE_TX_MEM_SIZE,0);
	m_log_console.p_uart = bsp_uart_init(LOG_CONSOLE_UART_CHN,LOG_CONSOLE_UART_BAUDRATE);
	m_log_console.mem_size=LOG_CONSOLE_TX_MEM_SIZE;
	m_log_console.mem_head=log_console_mem;
	m_log_console.mem_tail=log_console_mem + LOG_CONSOLE_TX_MEM_SIZE;
	m_log_console.txbuff_size=0;
	m_log_console.dma_tx_head=log_console_mem;
	m_log_console.dma_tx_size=0;
#ifndef BOOTLOADER
	bsp_uart_tx_dma_init(m_log_console.p_uart,log_console_tc_callback);
#endif
}
void log_tx_cb_reg(log_tx_callback callback)
{
	console_tx_callback = callback;
}
static void log_console_tc_callback(void* arg,uint8_t * txbuff,uint16_t tx_len)
{
#ifdef RTOS
	rt_base_t it = rt_hw_interrupt_disable();
	
#else
	__disable_irq();
#endif
	m_log_console.dma_tx_head+=tx_len;
	m_log_console.txbuff_size-=tx_len;
	m_log_console.dma_tx_size=0;
	__HAL_DMA_SET_COUNTER(&(m_log_console.p_uart->hal_tx_dma_handler),0);
//	uint16_t dma_left=__HAL_DMA_GET_COUNTER(&(m_log_console.p_uart->hal_tx_dma_handler));
//	if(dma_left == 0){
		if(m_log_console.dma_tx_head >= m_log_console.mem_tail)
			m_log_console.dma_tx_head = m_log_console.mem_head;
		log_console_start_transfer(&m_log_console);
//	}
#ifdef RTOS
	rt_hw_interrupt_enable(it);
#else
	__enable_irq();
#endif
}
void log_console_output(uint8_t * buff,uint16_t size)
{
#ifdef BOOTLOADER
	bsp_uart_transfer(m_log_console.p_uart,buff,size);
#else
	uint16_t write_size;
	uint8_t* p_src = buff;
	uint16_t remain = size;
	uint8_t* p_dst = m_log_console.dma_tx_head + m_log_console.txbuff_size;
	if(NULL==buff || remain ==0)
	{
		return;
	}
	if(remain > (m_log_console.mem_size-m_log_console.txbuff_size))
	{
		return;
	}
	/*找到尾部*/
	if(p_dst >= m_log_console.mem_tail)
	{
		p_dst = (p_dst - m_log_console.mem_tail) + m_log_console.mem_head;
	}
	if(p_dst + remain >= m_log_console.mem_tail)
	{
		write_size = m_log_console.mem_tail - p_dst;
		memcpy(p_dst,p_src,write_size);
		p_src += write_size;
		remain -= write_size;
		m_log_console.txbuff_size += write_size;
		p_dst = m_log_console.mem_head;
	}
	if(remain)
	{
		write_size = remain;
		memcpy(p_dst,p_src,write_size);
		p_src += write_size;
		remain -= write_size;
		m_log_console.txbuff_size += write_size;
		p_dst += write_size;
	}
	if(m_log_console.dma_tx_size == 0)
		log_console_start_transfer(&m_log_console);
	return;
#endif
}
static void log_console_start_transfer(log_console_t* p_log)
{
//	uint16_t dma_left=__HAL_DMA_GET_COUNTER(&(p_log->p_uart->hal_tx_dma_handler));
	if(p_log->txbuff_size != 0)
	{
		__HAL_DMA_DISABLE(&(p_log->p_uart->hal_tx_dma_handler));
		if((p_log->dma_tx_head + p_log->txbuff_size) > p_log->mem_tail)
		{
			p_log->dma_tx_size = p_log->mem_tail-p_log->dma_tx_head;
			bsp_uart_dma_transfer(p_log->p_uart,p_log->dma_tx_head,p_log->mem_tail-p_log->dma_tx_head);
		}
		else
		{
			p_log->dma_tx_size = p_log->txbuff_size;
			bsp_uart_dma_transfer(p_log->p_uart,p_log->dma_tx_head,p_log->txbuff_size);
		}
	}
}
void log_console_wait_cplt(void)
{
//#ifdef RTOS
//	rt_base_t it = rt_hw_interrupt_disable();
//#else
//	__disable_irq();
//#endif
	while(0 != __HAL_DMA_GET_COUNTER(&(m_log_console.p_uart->hal_tx_dma_handler)) 
		&& m_log_console.txbuff_size == 0)
	{
	}
//#ifdef RTOS
//	rt_hw_interrupt_enable(it);
//#else
//	__enable_irq();
//#endif
}

int log_print(uint8_t level,const char *file,int line,const char * fmt,...)
{
#ifdef RTOS
	rt_base_t it = rt_hw_interrupt_disable();
#else
	__disable_irq();
#endif
	va_list args;
	va_start(args, fmt);

	
	char *pc;
	uint16_t len = 0;
	uint16_t time_len;
	uint16_t temp_len;
	
	pc = log_cache;
	len = log_head_len[level];
	
	/* 头部信息 */
	memcpy(pc, log_head_group[level], len);
	pc += len;
	
	if(file != NULL)
	{
		temp_len = sprintf(pc,"FILE:%s,LINE:%d;",file,line);
		pc += temp_len;
		len += temp_len;
	}

#ifdef RTC_ON
	RTC_TimeTypeDef time = rtc_time(void);
	temp_len = sprintf(pc,"[H%02d,M%02d,S%02d]",time.Hours,time.Minutes,time.Seconds);
	pc += temp_len;
	len += temp_len;
#endif
	
	len += vsnprintf(pc,LOG_TTY_BUF_SIZE,fmt,args);
	va_end(args);
	ASSERT(len < LOG_TTY_BUF_SIZE);
	/* 输出全部内容 */
	log_console_output(log_cache, len);
	if(console_tx_callback != NULL)
	{
		console_tx_callback(log_cache,len);
	}
#ifdef RTOS
	rt_hw_interrupt_enable(it);
#else
	__enable_irq();
#endif
	return len;
}
void log_print_hex(uint8_t* buff,uint16_t size)
{
#ifdef RTOS
	rt_base_t it = rt_hw_interrupt_disable();
#else
	__disable_irq();
#endif
	char *pc = log_cache;
	uint16_t len = 0;
	uint32_t offset = 0;
	while(offset < size){
		len += sprintf(pc + len,"%02x ",buff[offset]);
		offset++;
		if(len > (LOG_TTY_BUF_SIZE - 10))
			break;
	}
	len += sprintf(pc + len,";\r\n");
	log_console_output(log_cache, len);
#ifdef RTOS
	rt_hw_interrupt_enable(it);
#else
	__enable_irq();
#endif
}
#ifdef 	LOG_ASSERT
void assert_failed(uint8_t* file, uint32_t line)
{
	log_print(ERROR_LEVEL,file,line,"assert failed;\r\n");
	__enable_irq();
	while(1){};
}
#endif
